package com.xlongwei.accounts.config;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.sql.DataSource;

import org.noear.solon.Solon;
import org.noear.solon.Utils;
import org.noear.solon.annotation.Component;
import org.noear.solon.core.bean.LifecycleBean;
import org.noear.solon.core.handle.Context;
import org.noear.solon.data.sql.SqlQuerier;
import org.noear.solon.data.sql.SqlSpec;
import org.noear.solon.data.sql.SqlUtils;
import org.noear.solon.data.sql.bound.RowConverter;

import cn.dev33.satoken.stp.StpUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 包装SQLException为RuntimeException
 */
@Slf4j
@Component
public class MySqlUtils implements LifecycleBean {
    public static final ThreadLocal<String> dsName = new ThreadLocal<>(); // 动态指定数据源（内部使用）
    public static final Map<String, DataSource> dsNames = new ConcurrentHashMap<>(); // 动态数据源集合
    public static final String dsDefault = Solon.cfg().get("accounts.dsName","accounts");

    @Override
    public void postStart() {
        // 动态订阅数据源，DsConfig新建数据源时需调context.beanPublish(dsBw);
        Solon.context().subWrapsOfType(DataSource.class, bw -> {
            MySqlUtils.dsNames.put(bw.name(), bw.get());
            log.info("{}={}", bw.name(), MySqlUtils.dsNames.get(bw.name()));
        });
    }

    public MySqlExecutor sql(SqlSpec sqlSpec) {
        return sql(sqlSpec.getSql(), sqlSpec.getArgs());
    }

    public MySqlExecutor sql(String sql, Object... args) {
        if (log.isDebugEnabled()) {
            log.debug(sql.replace("?", "'{}'"), args);
        }
        String dsName = dsNameOrDefault(dsName());
        DataSource dataSource = dsNames.get(dsName);
        return new MySqlExecutor(SqlUtils.of(dataSource).sql(sql, args));
    }

    public static String dsName() {
        String dsName = MySqlUtils.dsName.get();
        if(Utils.isNotBlank(dsName)) return dsName;
        try {
            if (StpUtil.isLogin()) {
                if(AppConfig.isSuperAdmin()) {
                    try {
                        dsName = Context.current().param("ds");
                    } catch (Exception e) {
                    }
                    if(Utils.isNotBlank(dsName)) return dsName;
                }
                String id = StpUtil.getLoginId().toString();
                int idx = id.indexOf('@');
                dsName = idx == -1 ? "" : id.substring(idx + 1);
                return dsName;
            }
        } catch (Exception e) {
        }
        try {
            dsName = Context.current().param("ds");
        } catch (Exception e) {
        }
        return dsName == null || "accounts".equals(dsName) ? "" : dsName;
    }
    // 在获取数据源时，空返回默认数据源更好一点
    public static String dsNameOrDefault(String dsName) {
        return Utils.isBlank(dsName) || !dsNames.containsKey(dsName) ? dsDefault : dsName;
    }
    // 在计算登录id和mobileDs时，默认数据源返回空
    public static String dsNameOrBlank(String dsName) {
        return Utils.isBlank(dsName) || dsDefault.equals(dsName) ? "" : dsName;
    }
    // 计算登录id和mobileDs
    public static String dsNameSuffix(Object obj) {
        String dsName = dsNameOrBlank(dsName());
        return String.valueOf(obj) + (Utils.isBlank(dsName) ? "" : "@" + dsName);
    }

    /**
     * 需要SqlExecutor其他方法继续复制即可
     */
    public static class MySqlExecutor {
        private SqlQuerier sqlExecutor;

        public MySqlExecutor(SqlQuerier sqlExecutor) {
            this.sqlExecutor = sqlExecutor;
        }

        public <T> T queryValue() {
            try {
                return sqlExecutor.queryValue();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public <T> T queryRow(Class<T> tClass) {
            try {
                return sqlExecutor.queryRow(tClass);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public <T> List<T> queryRowList(Class<T> tClass) {
            try {
                return sqlExecutor.queryRowList(tClass);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public <T> List<T> queryRowList(RowConverter<T> converter) {
            try {
                return sqlExecutor.queryRowList(converter);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public int update() {
            try {
                return sqlExecutor.update();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public <T> T updateReturnKey() {
            try {
                return sqlExecutor.updateReturnKey();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

    }
}
